﻿// ===============================================================================
// XCI.NET 西安交通信息WinForm开发框架 2018 版权所有
// ===============================================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace XCI.Data
{
    /// <summary>
    /// 参数解析帮助类
    /// </summary>
    public static class ParamHelper
    {
        /// <summary>
        /// SQL参数匹配正则表达式
        /// </summary>
        public static readonly Regex ParamsPrefixRegex = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);

        /// <summary>
        /// 解析SQL语句参数
        /// </summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="argsSrc">用户传入的参数对象</param>
        /// <param name="argsDest">接收的参数列表</param>
        /// <returns></returns>
        public static string ParserParameter(string sql, object argsSrc, List<KeyValuePair<string, object>> argsDest)
        {
            if (argsSrc == null) return sql;

            if (argsSrc is object[] arrayArgs)
            {
                int paramIndex = -1;

                return ParamsPrefixRegex.Replace(sql, m =>
                {
                    paramIndex++;
                    string name = m.Value.Substring(1); //取参数名字,不带特殊标记@:
                    if (paramIndex < 0 || paramIndex >= arrayArgs.Length)
                        throw new ArgumentOutOfRangeException(nameof(argsSrc), "解析参数错误,参数数量与给定的参数值数量不符");
                    var value = arrayArgs[paramIndex];
                    return HandleParameter(name, value, argsDest);
                });
            }

            if (argsSrc is IDictionary<string, object> dicArgs)
            {
                return ParamsPrefixRegex.Replace(sql, m =>
                {
                    string name = m.Value.Substring(1); //取参数名字,不带特殊标记@:
                    dicArgs.TryGetValue(name, out var value);
                    return HandleParameter(name, value, argsDest);
                });
            }

            Type argsType = argsSrc.GetType();
            return ParamsPrefixRegex.Replace(sql, m =>
            {
                string name = m.Value.Substring(1); //取参数名字,不带特殊标记@:
                object value = null;
                PropertyInfo pinfo = argsType.GetProperty(name);
                if (pinfo != null)
                {
                    value = pinfo.GetValue(argsSrc, null);
                }
                return HandleParameter(name, value, argsDest);
            });
        }

        /// <summary>
        /// 添加参数到接收的参数列表
        /// </summary>
        /// <param name="name">参数名称</param>
        /// <param name="value">参数值</param>
        /// <param name="argsDest">接收的参数列表</param>
        /// <returns>返回替换的参数名称</returns>
        private static string HandleParameter(string name, object value, List<KeyValuePair<string, object>> argsDest)
        {
            if ((value as IEnumerable) != null &&
                (value as string) == null &&
                (value as byte[]) == null)
            {
                var sb = new StringBuilder();
                int index = 0;
                foreach (var i in (IEnumerable) value)
                {
                    string partName = name + index;
                    sb.Append((index == 0 ? "@" : ",@") + partName);
                    if (!argsDest.Exists(p => p.Key.Equals(partName)))
                    {
                        argsDest.Add(new KeyValuePair<string, object>(partName, i));
                    }
                    index++;
                }
                return sb.ToString();
            }

            if (!argsDest.Exists(p => p.Key.Equals(name)))
            {
                argsDest.Add(new KeyValuePair<string, object>(name, value));
            }
            return "@" + name;
        }

        /// <summary>
        /// 解析存储过程参数
        /// </summary>
        /// <param name="argsSrc">用户传入的参数对象</param>
        /// <param name="argsDest">接收的参数列表</param>
        public static void ParserStoreProcParameter(object argsSrc, List<KeyValuePair<string, object>> argsDest)
        {
            if (argsSrc == null) return;
            if (argsSrc is object[])
            {
                throw new Exception("此方法不支持数组值类型参数");
            }

            if (argsSrc is IDictionary<string, object> dicArgs)
            {
                foreach (var pair in dicArgs)
                {
                    string name = pair.Key;
                    if (!argsDest.Exists(p => p.Key.Equals(name)))
                    {
                        argsDest.Add(new KeyValuePair<string, object>(name, pair.Value));
                    }
                }
            }
            else
            {
                foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(argsSrc))
                {
                    string name = propertyDescriptor.Name;
                    if (!argsDest.Exists(p => p.Key.Equals(name)))
                    {
                        argsDest.Add(new KeyValuePair<string, object>(name, propertyDescriptor.GetValue(argsSrc)));
                    }
                }
            }
        }
    }
}